{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Hysteresis State models"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.1.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m25.2\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install \"pybamm[plot,cite]\" -q    # install PyBaMM if it is not installed\n",
    "import pybamm"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Model Equations\n",
    "\n",
    "Herein, we outline the equations for the \"one-state hysteresis\" model (Axen, 2022), the \"one-state differential capacity hysteresis\" model (Wycisk, 2022), and the \"current Sigmoid\" model (Ai et al., 2022). In this notebook we collectively paraphrase all the original literature models into a common notation and definition. For the original model expressions please refer to the appropriate references.\n",
    "\n",
    "In all of the models, the open-circuit potential is given by\n",
    "\n",
    "$$U = \\frac{1+h}{2} U_{\\text{delith}} + \\frac{1-h}{2} U_{\\text{lith}},$$\n",
    "\n",
    "where $h$ is a hysteresis state variable, $U_{\\text{delith}}$ is the delithiation open-circuit potential, and $U_{\\text{lith}}$ is the lithiation open-circuit potential. The hysteresis state variable $h$ is defined between -1 and 1, where 1 corresponds to the delithiation branch and -1 corresponds to the lithiation branch.\n",
    "\n",
    "### One-state hysteresis (Axen)\n",
    "In this model, the state variable $h(z,t)$ is both stoichiometry and time-dependent, and is governed by the following ODE:\n",
    "$$ \\frac{dh(z,t)}{dt} = \\gamma \\left( \\frac{i_{\\text{vol}}}{F \\, c_{\\text{max}} \\, \\epsilon}\\right) \\frac{\\left(1 - \\text{sgn}(i_{\\text{vol}}) \\, h(z,t)\\right)}{2},$$\n",
    "where $i_{\\text{vol}}$ is the volumetric interfacial current density, $F$ is the Faraday constant, $c_{\\text{max}}$ is the maximum lithium concentration in the particles and $\\epsilon$ is the active material volume fraction. In this model, the rate parameter $\\gamma$ is given by\n",
    "$$\\gamma = \\left( \\frac{1-\\text{sgn}(i_{\\text{vol}})}{2}  \\gamma_{\\text{lith}} + \\frac{1+\\text{sgn}(i_{\\text{vol}})}{2} \\gamma_{\\text{delith}}\\right),$$\n",
    "which allows for a different decay rate during delithiation ($\\gamma_{\\text{delith}}$) and lithiation ($\\gamma_{\\text{lith}}$).\n",
    "Note that this implementation is a reformulation of the governing equations in the original paper. Also note that $\\gamma$ is dimensionless.\n",
    "\n",
    "\n",
    "### One-state differential capacity hysteresis (Wycisk)\n",
    "As in the Axen model, the state variable $h(z,t)$ is both stoichiometry and time-dependent, and is governed by the same ODE\n",
    "$$ \\frac{dh(z,t)}{dt} = \\gamma \\left( \\frac{i_{\\text{vol}}}{F \\, c_{\\text{max}} \\, \\epsilon}\\right) \\frac{\\left(1 - \\text{sgn}(i_{\\text{vol}}) \\, h(z,t)\\right)}{2},$$\n",
    "\n",
    "where now $\\gamma(z)$ is given by\n",
    "\n",
    "$$ \\gamma(z) = \\Gamma(z) \\cdot \\left(\\frac{C_{\\text{diff}}\\left(z\\right)}{C_{\\text{ref,vol}}}\\right)^{-x}.$$\n",
    "\n",
    "Here $C_{\\text{diff}}(z)$ is the differential capacity with respect to potential, expressed as \n",
    "\n",
    "$$ C_{\\text{diff}}(z) = \\frac{dQ}{dU_{\\text{eq}}(z)},$$\n",
    "where $Q$ is the capacity of the phase of active material experiencing the voltage hysteresis and $U_{\\text{eq}}$ is the average open-circuit potential of the phase of active material experiencing the voltage hysteresis. The remaining parameters are $\\Gamma$ and $x$ which are both fitting parameters that affect the response of the hysteresis state decay when passing charge in either direction. The parameter $C_{\\text{ref,vol}}$ is the reference differential capacity, which in this implementation is hard-coded to 1 $\\text{F}$.\n",
    "\n",
    "### Current Sigmoid (Ai)\n",
    "This approach uses a sigmoid function to switch between the delithiation and lithiation branches of the open-circuit potential, depending on the sign of the current. The original paper gives the following expression for the open-circuit potential:\n",
    "\n",
    "$$ U = \\text{sigmoid}\\left(-\\frac{KI(t)}{Q_{\\text{cell}}}\\right) U_{\\text{delith}} + \\text{sigmoid}\\left(\\frac{KI(t)}{Q_{\\text{cell}}}\\right) U_{\\text{lith}}\n",
    "$$\n",
    "\n",
    "Where $K$ is a fitting parameter (in the current implementation this is hard-coded to 100, as in the original publication), $I$ is the cell current, and $Q_{\\text{cell}}$ is the cell capacity. To simplify the comparison with the other models, we can rewrite this expression in terms of the hysteresis state variable $h$ given by\n",
    "\n",
    "$$h(t) = 1 - 2 \\, \\text{sigmoid}\\left(-\\frac{KI(t)}{Q_{\\text{cell}}}\\right).$$\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comparing the different hysteresis state models\n",
    "\n",
    "We now compare the behavior of the different hysteresis state models. First we define the models, which are all based on the DFN model with a composite negative electrode comprised of two particles phases. One phase is modelled using a single open-circuit potential, while the other is modelled using a hysteresis state model. The positive electrode is modelled using a single open-circuit potential."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "models = {\n",
    "    \"current sigmoid\": pybamm.lithium_ion.DFN(\n",
    "        {\n",
    "            \"open-circuit potential\": ((\"single\", \"current sigmoid\"), \"single\"),\n",
    "            \"particle phases\": (\"2\", \"1\"),\n",
    "            \"thermal\": \"lumped\",\n",
    "        }\n",
    "    ),\n",
    "    \"one-state differential capacity hysteresis\": pybamm.lithium_ion.DFN(\n",
    "        {\n",
    "            \"open-circuit potential\": (\n",
    "                (\"single\", \"one-state differential capacity hysteresis\"),\n",
    "                \"single\",\n",
    "            ),\n",
    "            \"particle phases\": (\"2\", \"1\"),\n",
    "            \"thermal\": \"lumped\",\n",
    "        }\n",
    "    ),\n",
    "    \"one-state hysteresis\": pybamm.lithium_ion.DFN(\n",
    "        {\n",
    "            \"open-circuit potential\": ((\"single\", \"one-state hysteresis\"), \"single\"),\n",
    "            \"particle phases\": (\"2\", \"1\"),\n",
    "            \"thermal\": \"lumped\",\n",
    "        }\n",
    "    ),\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we define the parameter set, which is for a graphite and silicon negative electrode with a NMC positive electrode. We add some thermal parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "parameters = pybamm.ParameterValues(\"Chen2020_composite\")\n",
    "parameters.update(\n",
    "    {\n",
    "        \"Negative current collector density [kg.m-3]\": 8933.0,\n",
    "        \"Negative current collector specific heat capacity [J.kg-1.K-1]\": 385.0,\n",
    "        \"Negative current collector thermal conductivity [W.m-1.K-1]\": 398.0,\n",
    "        \"Negative electrode density [kg.m-3]\": 1555.0,\n",
    "        \"Negative electrode specific heat capacity [J.kg-1.K-1]\": 1437.0,\n",
    "        \"Negative electrode thermal conductivity [W.m-1.K-1]\": 1.58,\n",
    "        \"Separator density [kg.m-3]\": 1017.0,\n",
    "        \"Separator specific heat capacity [J.kg-1.K-1]\": 1978.0,\n",
    "        \"Separator thermal conductivity [W.m-1.K-1]\": 0.34,\n",
    "        \"Positive electrode density [kg.m-3]\": 3699.0,\n",
    "        \"Positive electrode specific heat capacity [J.kg-1.K-1]\": 1270.0,\n",
    "        \"Positive electrode thermal conductivity [W.m-1.K-1]\": 1.04,\n",
    "        \"Positive current collector density [kg.m-3]\": 2702.0,\n",
    "        \"Positive current collector specific heat capacity [J.kg-1.K-1]\": 903.0,\n",
    "        \"Positive current collector thermal conductivity [W.m-1.K-1]\": 238.0,\n",
    "        \"Total heat transfer coefficient [W.m-2.K-1]\": 5.0,\n",
    "    },\n",
    "    check_already_exists=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we need to add the additional parameters required by the model. In this example, $K_{\\text{lith}} = K_{\\text{delith}} = 100$ for the HS model, but it is recommended to test different values for the decay rate aiming for good agreement to experiment if the information is available, or a smooth transition between the lithiation and delithiation branches of the hysteresis. For example, depending on the case study, an acceptable value for $K_{\\text{lith}}$ and $K_{\\text{delith}}$ could be $10$ or lower."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Wycisk parameters\n",
    "parameters_wycisk = parameters.copy()\n",
    "parameters_wycisk.update(\n",
    "    {\n",
    "        \"Secondary: Negative particle hysteresis decay rate\": 0.1,\n",
    "        \"Secondary: Negative particle hysteresis switching factor\": 10,\n",
    "        \"Secondary: Initial hysteresis state in negative electrode\": 1,\n",
    "    },\n",
    "    check_already_exists=False,\n",
    ")\n",
    "\n",
    "# Axen parameters\n",
    "parameters_axen = parameters.copy()\n",
    "parameters_axen.update(\n",
    "    {\n",
    "        \"Secondary: Negative particle lithiation hysteresis decay rate\": 100,\n",
    "        \"Secondary: Negative particle delithiation hysteresis decay rate\": 100,\n",
    "        \"Secondary: Initial hysteresis state in negative electrode\": 1,\n",
    "    },\n",
    "    check_already_exists=False,\n",
    ")\n",
    "\n",
    "parameter_values = {\n",
    "    \"current sigmoid\": parameters,\n",
    "    \"one-state differential capacity hysteresis\": parameters_wycisk,\n",
    "    \"one-state hysteresis\": parameters_axen,\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Then we define the experiment, which is a discharge and charge cycle at 1C."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "experiment = pybamm.Experiment(\n",
    "    [\n",
    "        (\n",
    "            \"Discharge at 1C for 1 hour or until 2.5 V\",\n",
    "            \"Rest for 15 minutes\",\n",
    "            \"Charge at 1C until 4.2 V\",\n",
    "            \"Hold at 4.2 V until 0.05 C\",\n",
    "            \"Rest for 15 minutes\",\n",
    "        ),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We then loop over the models and solve them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "solutions = {}\n",
    "for name, model in models.items():\n",
    "    sim = pybamm.Simulation(\n",
    "        model, experiment=experiment, parameter_values=parameter_values[name]\n",
    "    )\n",
    "    solutions[name] = sim.solve(calc_esoh=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we plot the results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "5efa147c8cf44c7db96697dfdda4e610",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "interactive(children=(FloatSlider(value=0.0, description='t', max=3.041045468063589, step=0.03041045468063589)…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<pybamm.plotting.quick_plot.QuickPlot at 0x32a3cf2f0>"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "output_variables = [\n",
    "    \"Current [A]\",\n",
    "    \"Negative electrode secondary stoichiometry\",\n",
    "    \"Voltage [V]\",\n",
    "    \"Volume-averaged cell temperature [K]\",\n",
    "    \"Total heating [W.m-3]\",\n",
    "    \"Hysteresis electrochemical heating [W.m-3]\",\n",
    "    \"X-averaged negative electrode secondary hysteresis state\",\n",
    "    \"X-averaged negative electrode secondary open-circuit potential [V]\",\n",
    "]\n",
    "\n",
    "pybamm.dynamic_plot(\n",
    "    list(solutions.values()),\n",
    "    labels=list(solutions.keys()),\n",
    "    colors=[\"black\", \"blue\", \"red\"],\n",
    "    linestyles=[\"-\", \"--\", \"-.\"],\n",
    "    output_variables=output_variables,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see that both one-state hysteresis models include a decay time for the hysteresis state variable $h$, and that they \"stick\" the (de)lithiation branches during rest. This is in contrast to the current sigmoid model, which does not stick to the (de)lithiation branches during rest; it instantly switches back the the equilibrium open-circuit potential. The effect of this can clearly be seen in the voltage and open-circuit potential plots."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".env",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
